package net.disy.cadenza.repository.wrappers;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.xml.bind.JAXBException;
import javax.xml.stream.XMLStreamException;

import net.disy.cadenza.dbmapping.schema.AttributeMapping;
import net.disy.cadenza.dbmapping.schema.DatabaseMapping;
import net.disy.cadenza.dbmapping.schema.Source;
import net.disy.cadenza.objecttypes.schema.CadenzaObjectType;
import net.disy.cadenza.repository.parser.RepositoryParser;
import net.disy.cadenza.repository.selector.ResultObjectTypes;
import net.disy.cadenza.repository.selector.ResultTable;
import net.disy.cadenza.repository.workflow.Workflow;
import net.disy.cadenza.repository.workflow.WorkflowList;
import net.disy.cadenza.repositorytree.schema.Entry;
import net.disy.cadenza.repositorytree.schema.Folder;
import net.disy.cadenza.repositorytree.schema.Item;

public class RepositoryTreeWrapper {
	private RepositoryParser repositoryParser;
	private Folder repositoryTreeRootFolder;
	private List<Item> items;
	private List<Item> selectorItems;
	private List<Item> workflowItems;
	private SelectorListWrapper selectorList;
	private SelectorListWrapper referencedSelectorList;
	private WorkflowList referencedWorkflowList;
	private ObjectTypeCatalogWrapper objectTypeCatalog;
	
	public RepositoryTreeWrapper(RepositoryParser repositoryParser) throws IOException, JAXBException, XMLStreamException{
		this.repositoryParser = repositoryParser;
		this.repositoryTreeRootFolder = repositoryParser.parseRepositoryTree();
		items = new ArrayList<Item>();
		items.addAll(getItemsFromFolder(this.repositoryTreeRootFolder, ""));
		selectorItems = findSelectorItems();
		workflowItems = findWorkflowItems();
		this.selectorList = this.repositoryParser.parseSelectors();
		this.objectTypeCatalog = this.repositoryParser.parseObjectTypeCatalog();
		referencedWorkflowList = findReferencedWorkflows();
		referencedSelectorList = findReferencedSelectors();
		for(Workflow workflow : referencedWorkflowList.getWorkflows()){
			if(workflow.getSelectorName() != null){
				workflow.setSelector(referencedSelectorList.getSelector(workflow.getSelectorName()));
			}
		}
	}
	
	public ObjectTypeCatalogWrapper getObjectTypeCatalog(){
		return objectTypeCatalog;
	}
	
	public SelectorListWrapper getReferencedSelectorList() {
		return referencedSelectorList;
	}
	
	public Set<String> getReferencedObjectTypeNames(){
		Set<String> objectTypeNames = new HashSet<String>();
		for(SelectorWrapper selector : referencedSelectorList.getSelectors()){
			if(selector.isBaseSelector()){
				objectTypeNames.addAll(selector.getObjectTypeNames());
			}
		}
		return objectTypeNames;
	}
	
	public RepositorySearchResult searchRepository(ResultTable resutlTable){
		Set<SelectorWrapper> allSelectors = new HashSet<SelectorWrapper>();
		
		Set<Workflow> workflows = new HashSet<Workflow>();
		Set<SelectorWrapper> selectors = new HashSet<SelectorWrapper>();
		
		ResultObjectTypes resultObjectTypes = convertToResultObjectTypes(resutlTable);
		
		for(SelectorWrapper selector : referencedSelectorList.getSelectors()){
			if(selector.isSatisfyingResultObjectTypes(resultObjectTypes)){
				allSelectors.add(selector);
			}
		}
		
		for(SelectorWrapper selector : allSelectors){
			Set<Workflow> workflowForSelector = referencedWorkflowList.getWorkflows(selector.getName());
			workflows.addAll(workflowForSelector);
			if(selector.getItem() != null)
				selectors.add(selector);
		}
		
		
		RepositorySearchResult repositorySearchResult = new RepositorySearchResult(workflows, selectors, resultObjectTypes);
		
		return repositorySearchResult;
	}

	
	
	
	private ResultObjectTypes convertToResultObjectTypes(ResultTable resultTable){
		Map<String, List<String>> objectAttributeToValues = new HashMap<String, List<String>>();
		Map<String, Set<String>> keywordToObjectAttributes = new HashMap<String, Set<String>>();
		for(String tableColumn : resultTable.getTableColumnToValues().keySet()){
			String tableName = tableColumn.substring(0, tableColumn.indexOf("."));
			String columnName = tableColumn.substring(tableColumn.indexOf(".") + 1);
			List<String> tableColumValues = resultTable.getTableColumnToValues().get(tableColumn);
			Set<CadenzaObjectType> objectTypes = objectTypeCatalog.getObjectTypesForTable(tableName);
			
			for(CadenzaObjectType  objectType : objectTypes){
				DatabaseMapping databaseMapping = (DatabaseMapping)objectType.getMappings().getAny();
				for(AttributeMapping attributeMapping : databaseMapping.getAttributeMapping()){
					Source attributeMappingSource = attributeMapping.getSource();
					if(attributeMappingSource != null){
						String attributeName = attributeMapping.getAttributeName();
						String columnNameToAttribute = attributeMappingSource.getColumn();
						if(columnNameToAttribute.equalsIgnoreCase(columnName) && objectAttributeToValues.get(attributeName) == null){
							objectAttributeToValues.put(attributeName, tableColumValues);
							if(resultTable.getKeywordToTableColumn().values().contains(tableColumn)){
								for(String keyword : resultTable.getKeywordToTableColumn().keySet()){
									if(resultTable.getKeywordToTableColumn().get(keyword).equalsIgnoreCase(tableColumn)){
										Set<String> objectTypeAttributesToKeyword = keywordToObjectAttributes.get(keyword);
										if(objectTypeAttributesToKeyword == null){
											objectTypeAttributesToKeyword = new HashSet<String>();
											keywordToObjectAttributes.put(keyword, objectTypeAttributesToKeyword);
										}
										objectTypeAttributesToKeyword.add(attributeName);
									}
										
								}
							}
						}
					}
				}
			}
		}
		ResultObjectTypes resultObjectTypes = new ResultObjectTypes(objectAttributeToValues, keywordToObjectAttributes);
		
		return resultObjectTypes;
	}

	private SelectorListWrapper findReferencedSelectors(){
		Map<String, SelectorWrapper> nameToReferencedSelectorMap = new HashMap<String, SelectorWrapper>();
		for(Item selectorItem : selectorItems){
			SelectorWrapper selectorWrapper = selectorList.getSelector(selectorItem.getName());
			selectorWrapper.setItem(selectorItem);
			nameToReferencedSelectorMap.put(selectorWrapper.getName(), selectorWrapper);
		}
		for(Workflow workflow : referencedWorkflowList.getWorkflows()){
			SelectorWrapper selectorWrapper = selectorList.getSelector(workflow.getSource().getResultSourceData().getSelectorName());
			nameToReferencedSelectorMap.put(selectorWrapper.getName(), selectorWrapper);
		}
		
		return new SelectorListWrapper(nameToReferencedSelectorMap);
	}
	
	private WorkflowList findReferencedWorkflows(){
		Map<String, Workflow> nameToReferencedWorkflowMap = new HashMap<String, Workflow>();
		for(Item workflowItem : workflowItems){
			Workflow workflow = repositoryParser.parseWorkflow(workflowItem.getName());
			if(workflow != null){
				nameToReferencedWorkflowMap.put(workflow.getName(), workflow);
				workflow.setItem(workflowItem);
			}
		}
		return new WorkflowList(nameToReferencedWorkflowMap);
	}
	
	
	private List<Item> findSelectorItems(){
		List<Item> selectorItems = new ArrayList<Item>();
		for(Item item : items)
			if(item.getType().equalsIgnoreCase("selector"))
				selectorItems.add(item);
		return selectorItems;
	}
	
	private List<Item> findWorkflowItems(){
		List<Item> workflowItems = new ArrayList<Item>();
		for(Item item : items)
			if(item.getType().equalsIgnoreCase("workflow"))
				workflowItems.add(item);
		return workflowItems;
	}
	
	private static List<Item> getItemsFromFolder(Folder folder, String parentRepositoryPath){
		List<Item> items = new ArrayList<Item>();
		for(Entry entry : folder.getFolderOrItem()){
			if(entry.getProperties() != null && entry.getProperties().getIsVisibleInNavigator() != null && entry.getProperties().getIsVisibleInNavigator().isSetValue() && !entry.getProperties().getIsVisibleInNavigator().isValue()){
				continue;
			}
			if(entry instanceof Item){
				((Item)entry).setId(parentRepositoryPath + "." + folder.getPrintName() + "."+ ((Item)entry).getName());
				items.add((Item)entry);
			}else{
				items.addAll(getItemsFromFolder((Folder)entry, parentRepositoryPath + (parentRepositoryPath == "" ? "" : ".") + folder.getPrintName()));
			}
		}
		return items;
	}
}
